import 'dart:io';

import 'package:eq/native/constants.dart';
import 'package:eq/src/core.dart';
import 'package:eq/src/interpreter.dart';
import 'package:eq/src/statement.dart';
import 'package:eq/src/utils.dart';

void addFileFunctions(Module module) {
  addNativeFunction('exists', _exists, module);
  addNativeFunction('existsSync', _existsSync, module);
  addNativeFunction('create', _create, module);
  addNativeFunction('createSync', _createSync, module);
  addNativeFunction('writeAsBytes', _writeAsBytes, module);
  addNativeFunction('writeAsBytesSync', _writeAsBytesSync, module);
  addNativeFunction('writeAsString', _writeAsString, module);
  addNativeFunction('writeAsStringSync', _writeAsStringSync, module);
  addNativeFunction('readAsBytes', _readAsBytes, module);
  addNativeFunction('readAsBytesSync', _readAsBytesSync, module);
  addNativeFunction('readAsString', _readAsString, module);
  addNativeFunction('readAsStringSync', _readAsStringSync, module);
  addNativeFunction('readAsLines', _readAsLines, module);
  addNativeFunction('readAsLinesSync', _readAsLinesSync, module);
  addNativeFunction('rename', _rename, module);
  addNativeFunction('renameSync', _renameSync, module);
  addNativeFunction('length', _length, module);
  addNativeFunction('lengthSync', _lengthSync, module);
  addNativeFunction('copy', _copy, module);
  addNativeFunction('copySync', _copySync, module);
  addNativeFunction('lastAccessed', _lastAccessed, module);
  addNativeFunction('lastAccessedSync', _lastAccessedSync, module);
  addNativeFunction('setLastAccessedSync', _setLastAccessedSync, module);
  addNativeFunction('setLastAccessed', _setLastAccessed, module);
  addNativeFunction('lastModified', _lastModified, module);
  addNativeFunction('lastModifiedSync', _lastModifiedSync, module);
  addNativeFunction('setLastModified', _setLastModified, module);
  addNativeFunction('setLastModifiedSync', _setLastModifiedSync, module);
}

Value _existsSync(List<Value> args) {
  if (args.isNotEmpty && args[0].type == ValueType.STRING_VALUE) {
    File file = File(args[0].value);
    if (file.existsSync()) {
      return Value(ValueType.NATIVE_POINTER_VALUE, file, module: eq_file);
    }
  }
  return Value(ValueType.BOOLEAN_VALUE, false);
}

Value _exists(List<Value> args) {
  if (args.isNotEmpty && args[0].type == ValueType.STRING_VALUE) {
    File file = File(args[0].value);
    file.exists().then((value) {
      if (args.length > 1 && args[1].type == ValueType.FUNCTION_VALUE) {
        StatementResult result;
        List<Value> t1 = [Value(ValueType.BOOLEAN_VALUE, value)];

        if (value) {
          t1.add(Value(ValueType.NATIVE_POINTER_VALUE, file, module: eq_file));
        }
        result = functionCallbyName(
            args[1].path ?? args[1].module, args[1].value, t1);
        if (result.type == StatementResultType.ERROR_RUNTIME_RESULT) {
          exitApp(result.error!, 1);
        }
      }
    });
  }
  return Value(ValueType.BOOLEAN_VALUE, false);
}

Value _readAsBytesSync(List<Value> args) {
  if (args.isNotEmpty &&
      args[0].type == ValueType.NATIVE_POINTER_VALUE &&
      args[0].value is File) {
    File t1 = args[0].value;
    List t2 = t1.readAsBytesSync();
    List<Value> t3 = List.generate(
        t2.length, (index) => Value(ValueType.INT_VALUE, t2[index]));
    return Value(ValueType.ARRAY_VALUE, ArrayValue()..array.add(t3));
  }
  return Value(ValueType.NULL_VALUE, null);
}

Value _readAsBytes(List<Value> args) {
  if (args.isNotEmpty &&
      args[0].type == ValueType.NATIVE_POINTER_VALUE &&
      args[0].value is File) {
    File t1 = args[0].value;
    t1.readAsBytes().then((value) {
      if (args.length > 1 && args[1].type == ValueType.FUNCTION_VALUE) {
        StatementResult result;
        List<Value> t3 = List.generate(
            value.length, (index) => Value(ValueType.INT_VALUE, value[index]));
        result = functionCallbyName(args[1].path ?? args[1].module,
            args[1].value, [Value(ValueType.ARRAY_VALUE, t3)]);
        if (result.type == StatementResultType.ERROR_RUNTIME_RESULT) {
          exitApp(result.error!, 1);
        }
      }
    });
  }
  return Value(ValueType.NULL_VALUE, null);
}

Value _readAsStringSync(List<Value> args) {
  if (args.isNotEmpty &&
      args[0].type == ValueType.NATIVE_POINTER_VALUE &&
      args[0].value is File) {
    File t1 = args[0].value;
    return Value(ValueType.STRING_VALUE, t1.readAsStringSync());
  }
  return Value(ValueType.NULL_VALUE, null);
}

Value _readAsString(List<Value> args) {
  if (args.isNotEmpty &&
      args[0].type == ValueType.NATIVE_POINTER_VALUE &&
      args[0].value is File) {
    File t1 = args[0].value;
    t1.readAsString().then((value) {
      if (args.length > 1 && args[1].type == ValueType.FUNCTION_VALUE) {
        StatementResult result;

        result = functionCallbyName(args[1].path ?? args[1].module,
            args[1].value, [Value(ValueType.STRING_VALUE, value)]);
        if (result.type == StatementResultType.ERROR_RUNTIME_RESULT) {
          exitApp(result.error!, 1);
        }
      }
    });
  }
  return Value(ValueType.NULL_VALUE, null);
}

Value _readAsLinesSync(List<Value> args) {
  if (args.isNotEmpty &&
      args[0].type == ValueType.NATIVE_POINTER_VALUE &&
      args[0].value is File) {
    File t1 = args[0].value;
    List t2 = t1.readAsLinesSync();
    List<Value> t3 = List.generate(
        t2.length, (index) => Value(ValueType.STRING_VALUE, t2[index]));
    return Value(ValueType.ARRAY_VALUE, t3);
  }
  return Value(ValueType.NULL_VALUE, null);
}

Value _readAsLines(List<Value> args) {
  if (args.isNotEmpty &&
      args[0].type == ValueType.NATIVE_POINTER_VALUE &&
      args[0].value is File) {
    File t1 = args[0].value;
    t1.readAsLines().then((value) {
      if (args.length > 1 && args[1].type == ValueType.FUNCTION_VALUE) {
        StatementResult result;
        List<Value> t3 = List.generate(value.length,
            (index) => Value(ValueType.STRING_VALUE, value[index]));
        result = functionCallbyName(args[1].path ?? args[1].module,
            args[1].value, [Value(ValueType.ARRAY_VALUE, t3)]);
        if (result.type == StatementResultType.ERROR_RUNTIME_RESULT) {
          exitApp(result.error!, 1);
        }
      }
    });
  }
  return Value(ValueType.NULL_VALUE, null);
}

Value _writeAsBytesSync(List<Value> args) {
  if (args.length >= 2 &&
      args[0].type == ValueType.NATIVE_POINTER_VALUE &&
      args[0].value is File &&
      args[1].type == ValueType.ARRAY_VALUE) {
    File t1 = args[0].value;
    ArrayValue t2 = args[1].value;
    List<int> t3 = List.empty(growable: true);
    for (var element in t2.array[0]) {
      if (element.type != ValueType.INT_VALUE) {
        return Value(ValueType.BOOLEAN_VALUE, false);
      }
      t3.add(element.value);
    }
    t1.writeAsBytesSync(t3);
    return Value(ValueType.BOOLEAN_VALUE, true);
  }
  return Value(ValueType.BOOLEAN_VALUE, false);
}

Value _writeAsBytes(List<Value> args) {
  if (args.length >= 2 &&
      args[0].type == ValueType.NATIVE_POINTER_VALUE &&
      args[0].value is File &&
      args[1].type == ValueType.ARRAY_VALUE) {
    File t1 = args[0].value;
    List<int> t2 = List.empty(growable: true);
    ArrayValue t3 = args[1].value;
    for (var element in t3.array[0]) {
      if (element.type != ValueType.INT_VALUE) {
        return Value(ValueType.BOOLEAN_VALUE, false);
      }
      t2.add(element.value);
    }

    t1.writeAsBytes(t2).then((value) {
      if (args.length >= 3 && args[2].type == ValueType.FUNCTION_VALUE) {
        StatementResult result;
        result = functionCallbyName(args[2].path ?? args[2].module,
            args[2].value, [Value(ValueType.BOOLEAN_VALUE, true)]);
        if (result.type == StatementResultType.ERROR_RUNTIME_RESULT) {
          exitApp(result.error!, 1);
        }
      }
    });
  }
  return Value(ValueType.BOOLEAN_VALUE, false);
}

Value _writeAsStringSync(List<Value> args) {
  if (args.length >= 2 &&
      args[0].type == ValueType.NATIVE_POINTER_VALUE &&
      args[0].value is File &&
      args[1].type == ValueType.STRING_VALUE) {
    File t1 = args[0].value;
    t1.writeAsStringSync(args[1].value);
    return Value(ValueType.BOOLEAN_VALUE, true);
  }
  return Value(ValueType.BOOLEAN_VALUE, false);
}

Value _writeAsString(List<Value> args) {
  if (args.length >= 2 &&
      args[0].type == ValueType.NATIVE_POINTER_VALUE &&
      args[0].value is File &&
      args[1].type == ValueType.STRING_VALUE) {
    File t1 = args[0].value;

    t1.writeAsString(args[1].value).then((value) {
      if (args.length >= 3 && args[2].type == ValueType.FUNCTION_VALUE) {
        StatementResult result;
        result = functionCallbyName(args[2].path ?? args[2].module,
            args[2].value, [Value(ValueType.BOOLEAN_VALUE, true)]);
        if (result.type == StatementResultType.ERROR_RUNTIME_RESULT) {
          exitApp(result.error!, 1);
        }
      }
    });
  }
  return Value(ValueType.BOOLEAN_VALUE, false);
}

Value _rename(List<Value> args) {
  if (args.length >= 2 &&
      args[0].type == ValueType.NATIVE_POINTER_VALUE &&
      args[0].value is File &&
      args[1].type == ValueType.STRING_VALUE) {
    File t1 = args[0].value;
    t1.rename(args[1].value).then((value) {
      if (args.length >= 3 && args[2].type == ValueType.FUNCTION_VALUE) {
        StatementResult result;
        result = functionCallbyName(
            args[2].path ?? args[2].module,
            args[2].value,
            [Value(ValueType.NATIVE_POINTER_VALUE, value, module: eq_file)]);
        if (result.type == StatementResultType.ERROR_RUNTIME_RESULT) {
          exitApp(result.error!, 1);
        }
      }
    });
  }
  return Value(ValueType.BOOLEAN_VALUE, false);
}

Value _renameSync(List<Value> args) {
  if (args.length >= 2 &&
      args[0].type == ValueType.NATIVE_POINTER_VALUE &&
      args[0].value is File &&
      args[1].type == ValueType.STRING_VALUE) {
    File t1 = args[0].value;
    return Value(ValueType.NATIVE_POINTER_VALUE, t1.renameSync(args[1].value),
        module: eq_file);
  }
  return Value(ValueType.BOOLEAN_VALUE, false);
}

Value _length(List<Value> args) {
  if (args.isNotEmpty &&
      args[0].type == ValueType.NATIVE_POINTER_VALUE &&
      args[0].value is File) {
    File t1 = args[0].value;
    t1.length().then((value) {
      if (args.length >= 2 && args[1].type == ValueType.FUNCTION_VALUE) {
        StatementResult result;
        result = functionCallbyName(args[1].path ?? args[1].module,
            args[1].value, [Value(ValueType.INT_VALUE, value)]);
        if (result.type == StatementResultType.ERROR_RUNTIME_RESULT) {
          exitApp(result.error!, 1);
        }
      }
    });
  }
  return Value(ValueType.NULL_VALUE, null);
}

Value _lengthSync(List<Value> args) {
  if (args.isNotEmpty &&
      args[0].type == ValueType.NATIVE_POINTER_VALUE &&
      args[0].value is File) {
    File t1 = args[0].value;
    return Value(ValueType.INT_VALUE, t1.lengthSync());
  }
  return Value(ValueType.NULL_VALUE, null);
}

Value _create(List<Value> args) {
  if (args.isNotEmpty && args[0].type == ValueType.STRING_VALUE) {
    File file = File(args[0].value);
    file.create(recursive: true).then((value) {
      if (args.length >= 2 && args[1].type == ValueType.FUNCTION_VALUE) {
        StatementResult result;
        result = functionCallbyName(
            args[1].path ?? args[1].module,
            args[1].value,
            [Value(ValueType.NATIVE_POINTER_VALUE, value, module: eq_file)]);
        if (result.type == StatementResultType.ERROR_RUNTIME_RESULT) {
          exitApp(result.error!, 1);
        }
      }
    }, onError: (r) {
      if (args.length >= 2 && args[1].type == ValueType.FUNCTION_VALUE) {
        StatementResult result;
        result = functionCallbyName(args[1].path ?? args[1].module,
            args[1].value, [Value(ValueType.BOOLEAN_VALUE, false)]);
        if (result.type == StatementResultType.ERROR_RUNTIME_RESULT) {
          exitApp(result.error!, 1);
        }
      }
    });
  }
  return Value(ValueType.BOOLEAN_VALUE, false);
}

Value _createSync(List<Value> args) {
  if (args.isNotEmpty && args[0].type == ValueType.STRING_VALUE) {
    File file = File(args[0].value);
    try {
      file.createSync(recursive: true);
      return Value(ValueType.NATIVE_POINTER_VALUE, file, module: eq_file);
    } catch (e) {
      return Value(ValueType.BOOLEAN_VALUE, false);
    }
  }

  return Value(ValueType.BOOLEAN_VALUE, false);
}

Value _copy(List<Value> args) {
  if (args.length >= 2 &&
      args[0].type == ValueType.NATIVE_POINTER_VALUE &&
      args[0].value is File &&
      args[1].type == ValueType.STRING_VALUE) {
    File file = args[0].value;
    file.copy(args[1].value).then((value) {
      if (args.length >= 3 && args[2].type == ValueType.FUNCTION_VALUE) {
        StatementResult result;
        result = functionCallbyName(
            args[2].path ?? args[2].module,
            args[2].value,
            [Value(ValueType.NATIVE_POINTER_VALUE, value, module: eq_file)]);
        if (result.type == StatementResultType.ERROR_RUNTIME_RESULT) {
          exitApp(result.error!, 1);
        }
      }
    }, onError: (r) {
      if (args.length >= 3 && args[2].type == ValueType.FUNCTION_VALUE) {
        StatementResult result;
        result = functionCallbyName(args[2].path ?? args[2].module,
            args[2].value, [Value(ValueType.BOOLEAN_VALUE, false)]);
        if (result.type == StatementResultType.ERROR_RUNTIME_RESULT) {
          exitApp(result.error!, 1);
        }
      }
    });
  }
  return Value(ValueType.BOOLEAN_VALUE, false);
}

Value _copySync(List<Value> args) {
  if (args.length >= 2 &&
      args[0].type == ValueType.NATIVE_POINTER_VALUE &&
      args[0].value is File &&
      args[1].type == ValueType.STRING_VALUE) {
    File file = args[0].value;
    try {
      return Value(ValueType.NATIVE_POINTER_VALUE, file.copySync(args[1].value),
          module: eq_file);
    } catch (e) {
      return Value(ValueType.BOOLEAN_VALUE, false);
    }
  }
  return Value(ValueType.BOOLEAN_VALUE, false);
}

Value _lastAccessed(List<Value> args) {
  if (args.isNotEmpty &&
      args[0].type == ValueType.NATIVE_POINTER_VALUE &&
      args[0].value is File) {
    File file = args[0].value;
    file.lastAccessed().then((value) {
      if (args.length >= 2 && args[1].type == ValueType.FUNCTION_VALUE) {
        StatementResult result;
        result = functionCallbyName(
            args[1].path ?? args[1].module,
            args[1].value,
            [Value(ValueType.STRING_VALUE, value.toIso8601String())]);
        if (result.type == StatementResultType.ERROR_RUNTIME_RESULT) {
          exitApp(result.error!, 1);
        }
      }
    }, onError: (r) {
      if (args.length >= 2 && args[1].type == ValueType.FUNCTION_VALUE) {
        StatementResult result;
        result = functionCallbyName(args[1].path ?? args[1].module,
            args[1].value, [Value(ValueType.BOOLEAN_VALUE, false)]);
        if (result.type == StatementResultType.ERROR_RUNTIME_RESULT) {
          exitApp(result.error!, 1);
        }
      }
    });
  }
  return Value(ValueType.BOOLEAN_VALUE, false);
}

Value _lastAccessedSync(List<Value> args) {
  if (args.isNotEmpty &&
      args[0].type == ValueType.NATIVE_POINTER_VALUE &&
      args[0].value is File) {
    File file = args[0].value;
    try {
      return Value(
          ValueType.STRING_VALUE, file.lastAccessedSync().toIso8601String());
    } catch (e) {
      return Value(ValueType.BOOLEAN_VALUE, false);
    }
  }
  return Value(ValueType.BOOLEAN_VALUE, false);
}

Value _setLastAccessed(List<Value> args) {
  if (args.length >= 2 &&
      args[0].type == ValueType.NATIVE_POINTER_VALUE &&
      args[0].value is File &&
      args[1].type == ValueType.STRING_VALUE) {
    File file = args[0].value;

    DateTime? t1 = DateTime.tryParse(args[1].value);
    if (t1 == null) return Value(ValueType.BOOLEAN_VALUE, false);
    file.setLastAccessed(t1).then((value) {
      if (args.length >= 3 && args[2].type == ValueType.FUNCTION_VALUE) {
        StatementResult result;
        result = functionCallbyName(args[2].path ?? args[2].module,
            args[2].value, [Value(ValueType.BOOLEAN_VALUE, true)]);
        if (result.type == StatementResultType.ERROR_RUNTIME_RESULT) {
          exitApp(result.error!, 1);
        }
      }
    }, onError: (r) {
      if (args.length >= 3 && args[2].type == ValueType.FUNCTION_VALUE) {
        StatementResult result;
        result = functionCallbyName(args[2].path ?? args[2].module,
            args[2].value, [Value(ValueType.BOOLEAN_VALUE, false)]);
        if (result.type == StatementResultType.ERROR_RUNTIME_RESULT) {
          exitApp(result.error!, 1);
        }
      }
    });
  }
  return Value(ValueType.BOOLEAN_VALUE, false);
}

Value _setLastAccessedSync(List<Value> args) {
  if (args.length >= 2 &&
      args[0].type == ValueType.NATIVE_POINTER_VALUE &&
      args[0].value is File &&
      args[1].type == ValueType.STRING_VALUE) {
    File file = args[0].value;
    try {
      file.setLastAccessedSync(DateTime.parse(args[1].value));
      return Value(ValueType.BOOLEAN_VALUE, true);
    } catch (e) {
      return Value(ValueType.BOOLEAN_VALUE, false);
    }
  }
  return Value(ValueType.BOOLEAN_VALUE, false);
}

Value _lastModified(List<Value> args) {
  if (args.isNotEmpty &&
      args[0].type == ValueType.NATIVE_POINTER_VALUE &&
      args[0].value is File) {
    File file = args[0].value;
    file.lastModified().then((value) {
      if (args.length >= 2 && args[1].type == ValueType.FUNCTION_VALUE) {
        StatementResult result;
        result = functionCallbyName(
            args[1].path ?? args[1].module,
            args[1].value,
            [Value(ValueType.STRING_VALUE, value.toIso8601String())]);
        if (result.type == StatementResultType.ERROR_RUNTIME_RESULT) {
          exitApp(result.error!, 1);
        }
      }
    }, onError: (r) {
      if (args.length >= 2 && args[1].type == ValueType.FUNCTION_VALUE) {
        StatementResult result;
        result = functionCallbyName(args[1].path ?? args[1].module,
            args[1].value, [Value(ValueType.BOOLEAN_VALUE, false)]);
        if (result.type == StatementResultType.ERROR_RUNTIME_RESULT) {
          exitApp(result.error!, 1);
        }
      }
    });
  }
  return Value(ValueType.BOOLEAN_VALUE, false);
}

Value _lastModifiedSync(List<Value> args) {
  if (args.isNotEmpty &&
      args[0].type == ValueType.NATIVE_POINTER_VALUE &&
      args[0].value is File) {
    File file = args[0].value;
    try {
      return Value(
          ValueType.STRING_VALUE, file.lastModifiedSync().toIso8601String());
    } catch (e) {
      return Value(ValueType.BOOLEAN_VALUE, false);
    }
  }
  return Value(ValueType.BOOLEAN_VALUE, false);
}

Value _setLastModified(List<Value> args) {
  if (args.length >= 2 &&
      args[0].type == ValueType.NATIVE_POINTER_VALUE &&
      args[0].value is File &&
      args[1].type == ValueType.STRING_VALUE) {
    File file = args[0].value;

    DateTime? t1 = DateTime.tryParse(args[1].value);
    if (t1 == null) return Value(ValueType.BOOLEAN_VALUE, false);
    file.setLastModified(t1).then((value) {
      if (args.length >= 3 && args[2].type == ValueType.FUNCTION_VALUE) {
        StatementResult result;
        result = functionCallbyName(args[2].path ?? args[2].module,
            args[2].value, [Value(ValueType.BOOLEAN_VALUE, true)]);
        if (result.type == StatementResultType.ERROR_RUNTIME_RESULT) {
          exitApp(result.error!, 1);
        }
      }
    }, onError: (r) {
      if (args.length >= 3 && args[2].type == ValueType.FUNCTION_VALUE) {
        StatementResult result;
        result = functionCallbyName(args[2].path ?? args[2].module,
            args[2].value, [Value(ValueType.BOOLEAN_VALUE, false)]);
        if (result.type == StatementResultType.ERROR_RUNTIME_RESULT) {
          exitApp(result.error!, 1);
        }
      }
    });
  }
  return Value(ValueType.BOOLEAN_VALUE, false);
}

Value _setLastModifiedSync(List<Value> args) {
  if (args.length >= 2 &&
      args[0].type == ValueType.NATIVE_POINTER_VALUE &&
      args[0].value is File &&
      args[1].type == ValueType.STRING_VALUE) {
    File file = args[0].value;
    try {
      file.setLastModifiedSync(DateTime.parse(args[1].value));
      return Value(ValueType.BOOLEAN_VALUE, true);
    } catch (e) {
      return Value(ValueType.BOOLEAN_VALUE, false);
    }
  }
  return Value(ValueType.BOOLEAN_VALUE, false);
}
